package Persistance;

import Domain.IRankedItemsSet;
import Persistance.Exceptions.UnableToPredictRankException;
import org.apache.log4j.Logger;

import java.util.Collection;


public class CollaborationAlgo implements IFilteringAlgorithmStrategy {

    private static Logger logger = Logger.getLogger("MovieSystem.Persistance.CollaborationAlgo");

    IRankedItemsSet m_rs;

    public CollaborationAlgo(IRankedItemsSet rs) {
        m_rs = rs;
    }

    public double getPredictionRating(String user, String item) throws UnableToPredictRankException {
        logger.info("Starting calculation prediction rating ("+user+","+item+").");

        Collection<String> otherUsers = m_rs.getUsers();
        if (otherUsers.contains(user))
            otherUsers.remove(user);
        else {
            logger.info("No recommendations of user: "+user);
            throw new UnableToPredictRankException(user,item);
        }
        double ans = 0;
        double weightSum = 0;
        double userAvgRank = m_rs.getAvgRank(user);

        for(String u: otherUsers){
            if (m_rs.hasRank(u,item)){
                double weight = getWeight(user, u);
                double rank = m_rs.getRank(u, item) - m_rs.getAvgRank(u);
                logger.debug("second user is "+u+", weight = "+weight+" ,  rank = "+rank);

                weightSum += Math.abs(weight);

                ans += weight * rank;
            }
        }
        logger.debug("weightSum = "+weightSum+" ,  ans = "+ans);

        if (weightSum == 0){
            logger.info("Cannot predict user rank ("+user+","+item+") , WeightSum is 0");
            throw new UnableToPredictRankException(user,item);
        }
        ans = userAvgRank + (1.0/weightSum)*ans;
        logger.info("Predicted rating ("+user+","+item+") = "+ans);
        return ans;
    }

    private double getWeight(String user1, String user2){
        logger.info("Starting calculation of weight ("+user1+","+user2+").");
        
        Collection<String> jointMovies = m_rs.getJointItems(user1, user2);
        double userAvgRank1 = m_rs.getAvgRank(user1);
        double userAvgRank2 = m_rs.getAvgRank(user2);

        double numerator = 0;
        double square1 = 0;
        double square2 = 0;

        for(String item : jointMovies){
            if (m_rs.hasRank(user1,item) && m_rs.hasRank(user2,item)){
                double tmp1 = m_rs.getRank(user1, item) - userAvgRank1;
                double tmp2 = m_rs.getRank(user2, item) - userAvgRank2;
                
                numerator += tmp1 * tmp2;
                square1 += Math.pow(tmp1, 2);
                square2 += Math.pow(tmp2, 2);
            }
        }

        double ans = 0;
        if (square1*square2 != 0){
            ans = numerator / Math.sqrt(square1 * square2);
            logger.info("Weight ("+user1+","+user2+") = ("+numerator+"/"+Math.sqrt(square1 * square2)+") = "+ans);            
        }
        else{    
            logger.info("Cannot compute Weight ("+user1+","+user2+"). assuming 0.");
        }
        return ans;
    }

}